home *** CD-ROM | disk | FTP | other *** search
/ Beginning Mac Programming / Beginning Mac Programming.bin / pc / Open Me for REALbasic 3 / REALbasic 3.2 / Goodies / 3rd Party Demos / 3rd Party Plugins / TT's Plugin Starter / Plugin Source / Plugin Source.cpp < prev   
Encoding:
Text File  |  2001-01-22  |  16.5 KB  |  507 lines

  1. /*
  2.  * Starter for REALbasic 2.1 & 3.0 plugins
  3.  *
  4.  * This code shows how to add new global methods and how to create
  5.  * or extend classes to RB.
  6.  * Creating Controls is not shown in here - see the sample projects
  7.  * in RB's plugin SDK for that.
  8.  *
  9.  * This code is freeware, you may use and extend it as you like.
  10.  * Brought to by Thomas Tempelmann.
  11.  *
  12.  * The accompanying project file was created using CodeWarrior Pro 4
  13.  * with the latest updates (including IDE 3.3) from Metrowerks' FTP Site
  14.  * and Universal Headers 3.3.2 as well as CarbonLib 1.2 SDK from
  15.  * http://developer.apple.com/sdk/
  16.  *
  17.  * More RB-related stuff and updates can be found here:
  18.  *
  19.  *   <http://www.tempel.org/rb/>
  20.  *
  21.  * History:
  22.  * Nov 21, 99: Initial version.
  23.  * Jan 18, 2000 by TT: Verified with RB 2.1a25 that classes work
  24.  *   now in x86 plugins, too.
  25.  * Jan 28, 2000 by TT: ("Ruslan special") Now also initializes
  26.  *   static C++ constructors.
  27.  * Feb 1, 2000 by TT: Now also shows how to pass Boolean and
  28.  *   Double arguments. Also added "#pragma mpwc on" to this and to the
  29.  *   "PluginMain.cpp" for 68K compiles, and disabled the global MPW C
  30.  *   calling option in the project settings for "68K Processor".
  31.  *   Removed the "far" modifier from a function prototype to avoid an
  32.  *   error msg from the Pro 5's compiler.
  33.  * Mar 4, 2000 by TT: Added a BYREF parameter example.
  34.  * Jul 13, 2000 by TT: Updates for RB 2.1 - does not support
  35.  *   plugins for older RB versions any more.
  36.  * Jan 21, 2000 by TT: Added Carbon support (requires RB 3.0) and
  37.  *   did a lot of other improvements.
  38.  *
  39.  * Enjoy and contribute!
  40.  */
  41.  
  42.  
  43. //--------------------
  44. // Your includes here
  45. //--------------------
  46.  
  47.     // These and other includes should come _before_ the include of "rb_plugin.h"
  48.     // in order to avoid compilation errors.
  49.  
  50. #include <string.h>
  51. #if TARGET_OS_WIN32
  52.     static void inline DebugStr(unsigned char* p) {}
  53.     #define log(x) OutputDebugString(x)
  54.     #define PrepareCallback() 
  55.     #define EnterCallback()
  56.     #define ExitCallback()
  57. #else
  58.     #if TARGET_CPU_68K
  59.         #include <SetupA4.h>
  60.     #endif
  61.     #include <MacMemory.h>
  62.     #include "dcon.h"
  63.     #define log(x) dprintf(x)
  64. #endif
  65.  
  66. //-------------------------
  67. // RB header includes here
  68. //-------------------------
  69.  
  70.     // Note: these include should come last, after all other includes of yours
  71.  
  72. #include "TT's Plugin Support.h"
  73. #include "rb_plugin.h"
  74.  
  75.  
  76. //-----------------------
  77. // Some type definitions
  78. //-----------------------
  79.  
  80.     // "RBinteger" represents a RB "Integer" type:
  81.  
  82. typedef long RBinteger;
  83.  
  84.     // "RBbool..." represent a RB "Boolean" type; you need to watch the proper use in case of making 68K plugins:
  85.  
  86. typedef Boolean    RBboolVar;    // use this for Boolean variables stored in this plugin
  87. typedef Boolean    RBboolPar;    // use this to receive Boolean parameters (both by value and by reference)
  88. typedef long    RBboolRet;    // use this when returning a Boolean as a function result
  89.  
  90.     // "RBdouble" represents a RB "Double" type:
  91.  
  92. #if TARGET_CPU_68K
  93.     typedef Float80 RBdouble;    // Attention: for this to work, "Floating Point" in the "68K Processor" options must be set to "SANE".
  94. #else
  95.     typedef double RBdouble;
  96. #endif
  97.  
  98.     /*
  99.      * Note: Passing Singles to plugin functions is currently (RB 2.1.2) not correctly supported
  100.      * in RB for 68K code, so I suggest to always only use Doubles as parameters.
  101.      */
  102.  
  103.  
  104.  
  105. //-----------------------
  106. // Add global RB methods
  107. //-----------------------
  108.  
  109. static RBinteger sampleIntFunc (RBinteger a, RBinteger b)
  110. // Adds the two passed values and returns the result
  111. {
  112.     return a + b;
  113. }
  114.  
  115. #if TARGET_CPU_68K
  116.     static void sampleDblFunc (RBdouble *result, RBdouble a, RBdouble b)
  117.     {
  118.         *result = a + b;
  119.     }
  120. #else
  121.     static RBdouble sampleDblFunc (RBdouble a, RBdouble b)
  122.     {
  123.         return a + b;
  124.     }
  125. #endif
  126.  
  127. static void sampleIRfProc (RBinteger val, RBinteger *hi, RBinteger *lo)
  128. // Splits the passed integer into a higer and a lower 16 bit value, and returns them via the two BYREF parameters
  129. {
  130.     *hi = val >> 16;
  131.     *lo = val & 0xFFFF;
  132. }
  133.  
  134. #define copyBytes(from,to,amount) memcpy(to,from,amount)
  135.  
  136. static REALstring sampleStrFunc (REALstring a, REALstring b)
  137. // Concatenates the two strings and returns the result
  138. {
  139.     // get the length of the two strings
  140.     long la, lb;
  141.     la = a->Length();
  142.     lb = b->Length();
  143.     
  144.     // create a new string object that is large enough to hold the sum of the two strings
  145.     REALstring str = REALBuildString (nil, la + lb);
  146.     
  147.     if (str != nil) {
  148.         // now copy the two strings' contents into the new string buffer
  149.         copyBytes (a->CString(), (char*)str->CString(), la);
  150.         copyBytes (b->CString(), (char*)str->CString() + la, lb);
  151.     }
  152.     
  153.     return str;
  154. }
  155.  
  156. static void sampleSRfProc (RBinteger n, REALstring ch, REALstring *s)
  157. // Creates a new string in "s" that multiplies the character in "ch" by "n" times.
  158. // The string is a BYREF parameter in this case. Note how the old string is
  159. // disposed first before replacing it with the new instance.
  160. {
  161.     REALstring str = REALBuildString (nil, n);
  162.     if (str != nil) {
  163.         // fill the new string:
  164.         char theChar = ch->CString()[0];
  165.         memset ((char*)str->CString(), theChar, n);
  166.  
  167.         // we have to dispose of the passed string since we're replacing it with a newly created one:
  168.         REALUnlockString (*s);
  169.  
  170.         // now replace the string that was passed BYREF:
  171.         *s = str;
  172.     }
  173. }
  174.  
  175.  
  176. // For examples on handling other RB data types, such as Double and Boolean,
  177. // see the code for handling class properties further below.
  178.  
  179. static REALmethodDefinition pluginMethodArray[] = {
  180.     { (REALproc) sampleIntFunc, REALnoImplementation, "SampleIntFunc(A as Integer, B as Integer) as Integer" },
  181.     { (REALproc) sampleDblFunc, REALnoImplementation, "SampleDoubleFunc(A as Double, B as Double) as Double" },
  182.     { (REALproc) sampleStrFunc, REALnoImplementation, "SampleStrFunc(A as String, B as String) as String" },
  183.     { (REALproc) sampleIRfProc, REALnoImplementation, "SampleIntRefProc(val as Integer, BYREF Hi as Integer, BYREF Lo as Integer)" },
  184.     { (REALproc) sampleSRfProc, REALnoImplementation, "SampleStrRefProc(n as Integer, ch as String, BYREF s as String)" },
  185. };
  186.  
  187.  
  188. //------------------------------------------------------
  189. // Create a new RB class or extend an existing RB class
  190. //------------------------------------------------------
  191.  
  192.  
  193.     // If you want to create a new class, set the following macro
  194.     // to 0;
  195.     // If you want to extend an existing RB class, such as FolderItem,
  196.     // Date or Application, set the value to 1:
  197.  
  198. #define doExtendTheClass 0 // if 0, then it'll create a new class instead
  199.  
  200.  
  201. extern REALclassDefinition sampleClass;    // forward declaration
  202.  
  203.  
  204.     /*
  205.      * The following structure defines internal storage space for your
  206.      * class. This space is not automatically visible to the user, but
  207.      * only if you add property accessor routines like the ones below
  208.      * (see sampleIntGetter and following).
  209.      */
  210.  
  211. struct sampleClassData
  212. {
  213.     // Note: If you _extend_ a class, the properties here will be zeroed, but the
  214.     //       constructor will not be called (Note that I have not verified that
  215.     //       properties get actually zeroed, this is rather what RS' documentation
  216.     //       says. If you find that this is not correct, don't blame me, but let me
  217.     //       and RS know, please.)
  218.  
  219.     RBinteger    aInteger;
  220.     RBboolVar    aBoolean;    // make sure that this occupies a long in case you use REALstandardGetter() or REALstandardSetter()
  221.     double        aDouble;
  222. };
  223.  
  224.  
  225.     /*
  226.      * The following code implements the default constructor and destructor of the class.
  227.      *
  228.      * Note that those will only be called if you define a _new_ class, but not
  229.      * if you extends a class (this is a bug in RB and might perhaps get fixed later).
  230.      */
  231.  
  232. static void sampleClassConstructor (REALobject instance)
  233. {
  234.     ClassData (sampleClass, instance, sampleClassData, me);
  235.  
  236.     // you can initialize objects of "me" here ...
  237.     
  238.     me->aInteger = 1234;
  239. }
  240.  
  241. static void sampleClassDestructor (REALobject instance)
  242. {
  243.     ClassData (sampleClass, instance, sampleClassData, me);
  244.  
  245.     // close & dispose objects of "me" here
  246.     //
  247.     // (e.g., if you opened a file or allocated memory, you need to release it here again)
  248. }
  249.  
  250.  
  251.     /*
  252.      * The following code shows how to deal with RB's Integer values (which is quite simple)
  253.      */
  254.  
  255. static RBinteger sampleIntGetter (REALobject instance, long param)
  256. // This is called when RB reads a property of your class
  257. {
  258.     ClassData (sampleClass, instance, sampleClassData, me);
  259.     return me->aInteger;
  260. }
  261.  
  262. static void sampleIntSetter (REALobject instance, long param, RBinteger value)
  263. // This is called when RB assigns a value to a property of your class
  264. {
  265.     ClassData (sampleClass, instance, sampleClassData, me);
  266.     me->aInteger = value;
  267. }
  268.  
  269.  
  270.     /*
  271.      * The following code shows how to deal with RB's Boolean values. Note that RB stores
  272.      * its Boolean values as 8 bit (= 1 byte) values. However, if you return a Boolean
  273.      * value from a function, you must extend it to a long (32 bit) value, or you may get
  274.      * wrong results back. Using the "RBboolRet" type for function results takes care of
  275.      * that for you.
  276.      */
  277.  
  278. static RBboolRet sampleBoolGetter (REALobject instance, long param)
  279. // returns a boolean value as a function result
  280. {
  281.     ClassData (sampleClass, instance, sampleClassData, me);
  282.     return me->aBoolean != 0;    // 1 is "true", 0 is "false"
  283. }
  284.  
  285. static void sampleBoolSetter (REALobject instance, long param, RBboolPar value)
  286. {
  287.     ClassData (sampleClass, instance, sampleClassData, me);
  288.     me->aBoolean = (value != 0);
  289. }
  290.  
  291.  
  292.     /*
  293.      * The following code shows how to deal with RB's Double values - note that 68K code
  294.      * needs a special handling, because in 68K a RB double is a 80 bit value, while in
  295.      * PPC and x86 it is a 64 bit value:
  296.      */
  297.  
  298. #if TARGET_CPU_68K
  299.     static void sampleDoubleGetter (RBdouble* f, REALobject instance, long param)
  300.     {
  301.         ClassData (sampleClass, instance, sampleClassData, me);
  302.         *f = me->aDouble;
  303.         // note that here the RB's 80 bit "Float80" value is converted into C's 64 bit "double" value
  304.     }
  305. #else
  306.     static RBdouble sampleDoubleGetter (REALobject instance, long param)
  307.     {
  308.         ClassData (sampleClass, instance, sampleClassData, me);
  309.         return me->aDouble;
  310.     }
  311. #endif
  312.  
  313. static void sampleDoubleSetter (REALobject instance, long param, RBdouble value)
  314. {
  315.     ClassData (sampleClass, instance, sampleClassData, me);
  316.     me->aDouble = value;
  317.     // note for 68K: the 64 bit "double" value is converted into RB's 80 bit "extended" value here
  318. }
  319.  
  320.  
  321.  
  322.     /*
  323.      * Here's how you can implement callbacks into the BASIC code by providing an Event Handler
  324.      * in the class definition:
  325.      */
  326.  
  327. typedef RBinteger (*SampleEventProc) (REALobject theObjInst, RBinteger someValue);    // this is the C version of the "SampleEvent" declaration below, but with an added obj reference parm
  328.  
  329. static REALevent sampleClassEvents[] = {
  330.     // defines events, which is where the plugin can call back into the high-level RB application code:
  331.     { "SampleEvent(someValue as Integer) as Integer" },
  332. };
  333.  
  334. static RBinteger callSampleEvent (REALobject instance, RBinteger firstArgument)
  335. // Use a routine like this if you wish to call back into BASIC code through an Event Handler.
  336. // The "instance" parameter receives the object that contains the Event Handler definition.
  337. // The "firstArgument" parm gets passed to the Event Handler.
  338. // This function then returns the result from the Event call, or returns 0 if there's no Event Handler implemented
  339. {
  340.     RBinteger result;
  341.     
  342.     int idx = 0;            // this index selects from the sampleClassEvents array. 0 is the first entry.
  343.     SampleEventProc fp = (SampleEventProc) REALGetEventInstance ((REALcontrolInstance)instance, &sampleClassEvents[idx]);
  344.  
  345.     if (fp) {
  346.         // now call into the BASIC code
  347.         result = fp (instance, firstArgument);
  348.     } else {
  349.         // if fp is nil, then no code BASIC has been filled in for SampleEvent. No need to call it then, of course.
  350.         result = 0;
  351.     }
  352.     
  353.     return result;
  354. }
  355.  
  356.  
  357.  
  358.     /*
  359.      * The following code shows how to implement methods of a class
  360.      */
  361.  
  362. static void sampleClassMethod (REALobject instance, RBinteger aValue)
  363. // This is called when RB calls a method of your class
  364. // As a demonstration, this function calls then back into BASIC code through the "SampleEvent" Event Handler
  365. {
  366.     ClassData (sampleClass, instance, sampleClassData, me);
  367.     me->aInteger = callSampleEvent (instance, aValue);
  368. }
  369.  
  370. static void sampleBoolRefGetter (REALobject instance, RBboolPar *value)
  371. // returns a boolean value via a BYREF parameter
  372. {
  373.     ClassData (sampleClass, instance, sampleClassData, me);
  374.     *value = me->aBoolean;
  375. }
  376.  
  377.  
  378.     /*
  379.      * The following structures declare the class and its members
  380.      */
  381.  
  382. static REALproperty sampleClassProperties[] = {
  383.     // defines a property "SampleInt as Integer" (also see note below!):
  384.     { nil, "SampleInt", "Integer",         0, (REALproc) sampleIntGetter,    (REALproc) sampleIntSetter,    0 /* param */ },
  385.     { nil, "SampleDouble", "Double",       0, (REALproc) sampleDoubleGetter, (REALproc) sampleDoubleSetter, 0 /* param */ },
  386.     { nil, "SampleActiveBool", "Boolean",  0, (REALproc) sampleBoolGetter,   (REALproc) sampleBoolSetter,   0 /* param */ },
  387.     { nil, "SamplePassiveBool", "Boolean", 0, REALstandardGetter, REALstandardSetter, FieldOffset(sampleClassData, aBoolean) },
  388. };
  389.  
  390.     // Note about the above SampleActiveBool and SamplePassiveBool properties:
  391.     // If you do not need control over the events when your properties get read or set, you can use
  392.     // a simpler way that what's shown above in many cases: Instead of providing your own Getter/Setter
  393.     // functions, you can let RB handle the access to your properties, provided they have matching
  394.     // size. The SamplePassiveBool uses this alternate way, accessing the _same_ property of this object.
  395.  
  396.  
  397. static REALmethodDefinition sampleClassMethods[] = {
  398.     // defines methods:
  399.     { (REALproc) sampleBoolRefGetter, REALnoImplementation, "SampleGetBool(ByRef b as Boolean)"},
  400.     { (REALproc) sampleClassMethod, REALnoImplementation, "SampleMethod(aValue as Integer)"},
  401. };
  402.  
  403.  
  404.  
  405.  
  406. static REALclassDefinition sampleClass = {
  407.     kCurrentREALControlVersion,
  408.     #if doExtendTheClass
  409.         "aRBClassName",    // <- put the name of the to be extended class here, like "FolderItem"
  410.         nil,
  411.     #else
  412.         "SampleClass",    // <- this is the name of the class you're adding with this plugin
  413.         nil,            // <- name of its super class, in case this is not a base class
  414.     #endif
  415.     sizeof(sampleClassData),
  416.     0,
  417.     #if doExtendTheClass
  418.         // Unfortunately, for extended classes, we don't get called when an obj is con-/destructed.
  419.         // (this is a current RB restriction and might be changed in a later version, hopefully)
  420.         nil, nil,
  421.     #else
  422.         (REALproc) sampleClassConstructor, (REALproc) sampleClassDestructor,
  423.     #endif
  424.     sampleClassProperties,
  425.     sizeof(sampleClassProperties) / sizeof(REALproperty),
  426.     sampleClassMethods,
  427.     sizeof(sampleClassMethods) / sizeof(REALmethodDefinition),
  428.     sampleClassEvents,
  429.     sizeof(sampleClassEvents) / sizeof(REALevent),
  430.     nil, 0
  431. };
  432.  
  433.  
  434. // ---------------------------------
  435. // ----- Plugin Initialization -----
  436. // ---------------------------------
  437.  
  438.     /*
  439.      * The following two functions get called when the app, that used this plugin,
  440.      * starts up ("PluginInit") and when it quits ("PluginExit").
  441.      */
  442.     
  443. void PluginInit (void)
  444. {
  445.     /*
  446.      * You can init global data here, like installing a Timer Task.
  447.      * Note that this function gets called before PluginEntry ().
  448.      */
  449.  
  450.     //log ("[TT's Plugin Starter] PluginInit called\n");
  451. }
  452.  
  453. void PluginExit (void)
  454. {
  455.     /*
  456.      * You can do your cleanup here in case you did open persistent resources
  457.      * (like having a Timer Task or asynchronous functions pending).
  458.      */
  459.  
  460.     //log ("[TT's Plugin Starter] PluginExit called\n");
  461. }
  462.  
  463. void PluginEntry (void)
  464. {
  465.     InitTTsPluginSupport ();
  466.  
  467.     /*
  468.      * This is the main entry that is called when your plugin is loaded by REALbasic
  469.      * or when a built application that uses your plugin is launched.
  470.      *
  471.      * Here you tell RB about what your plugin provides. Call ...
  472.      *  • REALRegisterMethod() to add gobal methods
  473.      *  • REALRegisterClass() to add new classes
  474.      *  • REALRegisterClassExtension() to extent existing classes
  475.      *  • REALRegisterControl() to add new controls
  476.      */
  477.  
  478.     //log ("[TT's Plugin Starter] PluginEntry called\n");
  479.     
  480.     /*
  481.      * Prepare for potential callbacks.
  482.      * Explanation: If you have functions that are being called by Mac OS,
  483.      * such as async completion routines and timers, you need to call EnterCallback()
  484.      * at their beginning and ExitCallback() before returning from them.
  485.      * Otherwise, you may get crashes in 68K code. If you do not make a 68K plugin, you
  486.      * do not need to take care of this, however. See TT's EventMonitor for a complete
  487.      * example.
  488.      */
  489.     #if TARGET_CPU_68K
  490.         PrepareCallback ();
  491.     #endif
  492.     
  493.     // Use this code to make any of your class definitions known to RB:
  494.     #if doExtendTheClass
  495.         REALRegisterClassExtension (&sampleClass);
  496.     #else
  497.         REALRegisterClass (&sampleClass);
  498.     #endif
  499.  
  500.     // Use this code to make your global RB methods known to RB:
  501.     for (int i = 0; i < sizeof(pluginMethodArray) / sizeof(REALmethodDefinition); ++i) {
  502.         REALRegisterMethod (&pluginMethodArray[i]);
  503.     }
  504.     
  505.     //log ("[TT's Plugin Starter] PluginEntry done.\n");
  506. }
  507.